﻿using System;
using System.Collections.Generic;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using ExpressionEngine;
using ExpressionEngine.ValueProviders;

namespace TestExpressionParser
{
    [TestClass]
    public class ExpressionUnitTest
    {
        [TestMethod]
        public void TestMethodStaticExpressions()
        {
            Assert.AreEqual(0, Calculate("0"));

            Assert.AreEqual(0, Calculate("0+0"));

            Assert.AreEqual(0, Calculate("0+1-1"));

            Assert.AreEqual(0, Calculate("0+1-1*(-100+ -200--1*(301*1))"));

            Assert.AreEqual(-1234, Calculate("-1234"));

            Assert.AreEqual(-1233, Calculate("1-1234"));

            Assert.AreEqual(-0, Calculate("-1234+-1234+1234*2"));

            Assert.AreEqual(-39160636416, Calculate("23456*(1+23-123*(54-3)-98+23)*-12*-22"));

            Assert.AreEqual(1229994, Calculate("-1+-2+-3+-2323--1232323"));

            Assert.AreEqual(177, Math.Round(Calculate("300-123-2323/3001232323")));

            Assert.AreEqual(-3, Calculate("(1+2)*(3-(8/2))"));

            Assert.AreEqual(-1198, Calculate("-1 * (((10+20)+30)*20-8/4)"));

            Assert.AreEqual(1216.06, Calculate("((10.1+20.2)+30.3)*20.1-8/4"));
        }

        [TestMethod]
        public void TestMethodExpressionsWithVariables()
        {
            DictionaryValueProvider provider = new DictionaryValueProvider();
            provider["X"] = 10;
            provider["Y"] = 20;
            provider["Value1"] = 30;
            provider["PI"] = Math.PI;
            provider["Radius"] = 10;

            Assert.AreEqual(900, Calculate("(X+Y)*Value1", provider));
            Assert.AreEqual(314.16, Math.Round(Calculate("PI*Radius*Radius", provider), 2));
        }

        [TestMethod]
        public void TestMethodAggregateExpressions()
        {
            List<AggregateTestDataItem> items = new List<AggregateTestDataItem>(new[]
            {
                new AggregateTestDataItem { Field1 = 1,    Field2 = 2,    Field3 = 3,    Field4 = 4 },
                new AggregateTestDataItem { Field1 = 23.2, Field2 = 22.2, Field3 = 54.3, Field4 = 23.2 },
                new AggregateTestDataItem { Field1 = 65.2, Field2 = 85.6, Field3 = 0.25, Field4 = 5.6 },
                new AggregateTestDataItem { Field1 = 6.32, Field2 = 87.6, Field3 = 54.8, Field4 = 0.54 }
            });

            Assert.AreEqual(23.93, CalculateAggregate(items, "AVG(Field1)"));
            Assert.AreEqual(65.2, CalculateAggregate(items, "MAX(Field1)"));
            Assert.AreEqual(150.8, CalculateAggregate(items, "MAX(Field1 + Field2)"));
            Assert.AreEqual(216, CalculateAggregate(items, "MAX(Field1 + Field2) + MAX(Field1)"));
            Assert.AreEqual(26.2, CalculateAggregate(items, "MIN(Field1 + Field2) + MAX(Field4)"));
            Assert.AreEqual(629.95, Math.Round(CalculateAggregate(items, "SUM((Field1 + Field2) / Field3) + MAX(Field4)"), 2));
            Assert.AreEqual(4, CalculateAggregate(items, "COUNT()"));
        }

        private double Calculate(string expression, IValueProvider valProvider = null)
        {
            ExpressionEveluator exp = new ExpressionEveluator(expression);

            return exp.Evaluate(valProvider);
        }

        private double CalculateAggregate(IEnumerable<object> data, string expression)
        {
            AggregateExpressionEvaluator evaluator = new AggregateExpressionEvaluator(expression);

            foreach (object obj in data)
            {
                ReflectionValueProvider provider = new ReflectionValueProvider(obj);
                evaluator.RowChanged(provider);
            }

            return evaluator.GetResult();
        }
    }
}
